#include "Vision.h"

Vision::Vision(Map* const m):m_(m)
{
    resetVisible();
    visionCheat_=false;

}

Vision::~Vision()
{
    //dtor
}
void Vision::setVisibilite(position depart)
{
    vector<position> ligne,cercle;
    vector<position>::iterator itr,itr2;

    resetVisible();
   /* cercle=cercleVisible(depart);
    for(itr2=cercle.begin();itr2!=cercle.end();++itr2){
        processBresenhamLine(bresenhamLine(depart,*itr2));


    }*/


    for(int i=0;i<NX;i++){
        processBresenhamLine(bresenhamLine(depart,position(i,0)));
        processBresenhamLine(bresenhamLine(depart,position(i,NY-1)));
        processBresenhamLine(bresenhamLine(depart,position(0,i)));
        processBresenhamLine(bresenhamLine(depart,position(NX-1,i)));

    }





    postProcessVisible(depart);
}
// Process les position renvoyer par bresenhamLine pour les rendres visible
void Vision::processBresenhamLine(vector<position> ligne)
{
    vector<position>::iterator itr;
    for(itr=ligne.begin();itr!=ligne.end();++itr){
        if(m_->getTransparence(*itr))
                visible_[itr->y][itr->x]=true;
            else
                break;
    }

}
//Calcul une aprroximation de la ligne droite et s'arrete quand elle rencontre un objet non transparent
const vector<position> Vision::bresenhamLine(position depart,position fin)
{
    int dx =abs(fin.x-depart.x);
    int dy= abs(fin.y-depart.y);
    int sx=1;
    int sy=1;
    int err=dx-dy;
    int e2=0;
    int errorprev=err;
    vector<position> ligne;
    if(depart.x>fin.x)
        sx=-1;
    if(depart.y>fin.y)
        sy=-1;
    while(depart.x<NX && depart.y<NY){
        ligne.push_back(depart);
         if((depart==fin )|| !(m_->getTransparence(depart)))
            return ligne;
         e2=2*err;
         if(e2>-dy){
            err-=dy;
            depart.x+=sx;
            if(err+errorprev>=2*dy)
                ligne.push_back(position(depart.x-sx,depart.y));
            if (err+errorprev<=2*dy)
                ligne.push_back(position(depart.x,depart.y+sy));
        }
        if((depart==fin )|| !(m_->getTransparence(depart))){
            ligne.push_back(depart);
            return ligne;
        }
        if(e2<dx){
            err+=dx;
            depart.y+=sy;
            if(err+errorprev>=2*dx)
                ligne.push_back(position(depart.x+sx,depart.y));
            if (err+errorprev<=2*dx)
                ligne.push_back(position(depart.x,depart.y-sy));
        }
        errorprev=err;
    }
    return ligne;
}
//Remet à zéro la carte de ce qui est visible
void Vision::resetVisible()
{
    for(int i=0;i<NY;i++){
        for(int j=0;j<NX;j++){

            visible_[i][j]=false;
        }
    }
}
//Post process pour supprimer les artefacts qui peuvent apparaitre (typiquement mur non éclairé entre deux murs eclairé)
void Vision::postProcessVisible(position depart)
{
    int x=depart.x;
    int y=depart.y;

    for(int i=0;i<NY;i++){
        for(int j=0;j<NX;j++){
            if(visible_[i][j]==true && m_->isInterieur(j,i) ){
                if(!(m_->getTransparence(j,i+1)) && i>=y)
                    visible_[i+1][j]=true;
                if(!(m_->getTransparence(j,i-1)) && i<=y)
                    visible_[i-1][j]=true;
                if(!(m_->getTransparence(j+1,i)) && j>=x)
                    visible_[i][j+1]=true;
                if(!(m_->getTransparence(j-1,i)) && j<=x)
                    visible_[i][j-1]=true;
            }
        }
    }

}
//Active ou désactive la vision globale
void Vision::changeVisionCheat()
{
    visionCheat_=!(visionCheat_);

}

//Approximation du cercle
vector<position> Vision::cercleVisible(position centre)
{
  int x = 10;
  int y = 0;
  int x0=centre.x;
  int y0=centre.y;
  int rayonErr = 1-x;
  vector<position> cercle;

  while(x > y)
  {

    cercle.push_back(position(x + x0, y + y0));
    cercle.push_back(position(y + x0, x + y0));
    cercle.push_back(position(-x + x0, y + y0));
    cercle.push_back(position(-y + x0, x + y0));
    cercle.push_back(position(-x + x0, -y + y0));
    cercle.push_back(position(-y + x0, -x + y0));
    cercle.push_back(position(x + x0, -y + y0));
    cercle.push_back(position(y + x0, -x + y0));

    y++;
        if(rayonErr<0)
                rayonErr+=2*y+1;
        else
        {
                x--;
                rayonErr+=2*(y-x+1);
        }
  }
  return cercle;
}
bool Vision::getVisibilite(int x,int y)
{
    if(visionCheat_)
        return true;
    else
        return visible_[y][x];
}
